/*
 * funcionesPRAID.c
 *
 *  Created on: 10/10/2011
 *      Author: utn_so
 */

#include "libPRAID.h"
#include "config.h"

void inicializarSemaforosYMutex ()
{
	extern pthread_mutex_t bloqueaColaGral;
	extern pthread_mutex_t bloqueaListaDiscos;
	extern sem_t semaforoLista;
	pthread_mutex_init (&bloqueaColaGral, NULL);
	pthread_mutex_init (&bloqueaListaDiscos, NULL);
	int32_t valorRetorno;
	valorRetorno = sem_init(&semaforoLista, 0, 0);
}

void crearColaGral ()
{
	extern tCola *colaGral;
	colaGral = (tCola *)calloc(1,sizeof(tCola));
	colaGral->ant=NULL;
	colaGral->post=NULL;
}

void crearListaDiscos()
{
	extern tLista *listaDiscos;
	listaDiscos = (tLista *)calloc(1,sizeof(tLista));
}

tCola *crearColaPeticiones ()
{
	tCola *colaPeticiones;
	colaPeticiones = (tCola *)calloc(1,sizeof(tCola));
	if (colaPeticiones == NULL)
		perror("Memoria insuficiente");
	colaPeticiones->ant = colaPeticiones->post = (tNodo *)calloc(1,sizeof(tNodo));
	if (colaPeticiones->ant == NULL)
		perror("Memoria insuficiente");
	colaPeticiones->ant = NULL;
	colaPeticiones->post = NULL;
	return colaPeticiones;
}

void discosConectados ()
{
	extern tLista *listaDiscos;
	extern int32_t cantDiscos;
	tNodoLista *ptrNodo = NULL;
	ptrNodo = listaDiscos->head;
	cantDiscos = 0;
	while (ptrNodo != NULL)
	{
		cantDiscos++;
		ptrNodo = ptrNodo->siguiente;
	}
}

 /* Funciones encolar y desencolar */

void encolarPeticionColaGral (int32_t socket, int32_t estado, t_NIPC paquete)
{
	extern tCola *colaGral;
	extern pthread_mutex_t bloqueaColaGral;
	int32_t bloquearCola;

	tNodo *nodo = (tNodo *) calloc (1, sizeof(tNodo));
	nodo->estado = estado;
	nodo->nroSocketFS = socket;
	NIPC_crearpaquete(paquete.DescriptorID,paquete.PayloadDescriptor,paquete.PayloadLength,paquete.payload,paquete.nroSector,&nodo->paquete);
	nodo->siguiente = NULL;

	bloquearCola = pthread_mutex_lock(&bloqueaColaGral);
	if (colaGral->ant == NULL)
		colaGral->ant = nodo;
	else
		colaGral->post->siguiente = nodo;
	colaGral->post = nodo;
	bloquearCola = pthread_mutex_unlock(&bloqueaColaGral);
}

void desencolarPeticionColaGral (int32_t *socket, int32_t *estado, t_NIPC *paquete)
{
    extern tCola *colaGral;
    extern pthread_mutex_t bloqueaColaGral;
    int32_t bloquearCola;
    tNodo *nodo;

    bloquearCola = pthread_mutex_lock (&bloqueaColaGral);
    nodo = colaGral->ant;
    if (colaGral->ant == colaGral->post)
    	colaGral->ant = NULL;
    else
    	colaGral->ant = colaGral->ant->siguiente;
    nodo->siguiente = NULL;
    if (colaGral->ant == NULL)
    	colaGral->post = NULL;

    *estado = nodo->estado;
    *socket = nodo->nroSocketFS;
    NIPC_crearpaquete(nodo->paquete.DescriptorID,nodo->paquete.PayloadDescriptor,nodo->paquete.PayloadLength,nodo->paquete.payload,nodo->paquete.nroSector,paquete);
    NIPC_destruirpaquete(&nodo->paquete);
    free(nodo);
    bloquearCola = pthread_mutex_unlock (&bloqueaColaGral);

}

void encolarPeticionColaDisco (tCola *cola, int32_t socket, int32_t estado, t_NIPC paquete)
{
	tNodo *nodo = (tNodo *) calloc (1, sizeof(tNodo));
	nodo->estado = estado;
	nodo->nroSocketFS = socket;

	NIPC_crearpaquete(paquete.DescriptorID,paquete.PayloadDescriptor,paquete.PayloadLength,paquete.payload,paquete.nroSector,&nodo->paquete);

	nodo->siguiente = NULL;

	if (cola->ant == NULL)
	{
		cola->ant = nodo;
		cola->post = nodo;
	}
	else
	{
		cola->post->siguiente = nodo;
		cola->post = nodo;
	}

}


void buscarNodosSinSincroYEncola (int32_t socket, int32_t estado, t_NIPC paquete)
{
	extern tLista *listaDiscos;
	extern int32_t cantDiscos;
	extern tConfig config;
	extern t_log* loguear;
	extern int32_t sincro;
	tNodoLista *nodoAux = NULL;
	int32_t i=0;
	int32_t bloquearCola;

	nodoAux = listaDiscos->head;
	discosConectados();

	while (i < cantDiscos && nodoAux != NULL)
	{
		if (nodoAux->sincro != sincro)
		{
			bloquearCola = pthread_mutex_lock(&(nodoAux->semaforos.bloqueaColaDisco));
			bloquearCola = pthread_mutex_lock(&(nodoAux->semaforos.bloqueaCantPedidos));
			encolarPeticionColaDisco (nodoAux->colaDisco, socket, estado, paquete);
			if (config.flagConsola == 1)
				log_info(loguear, "PRAID", "Se hizo un write en el disco %d", nodoAux->idDisco);
			nodoAux->cantPedidos++;
			sem_post(&nodoAux->semaforos.semaforoDisco);
			bloquearCola = pthread_mutex_unlock(&(nodoAux->semaforos.bloqueaCantPedidos));
			bloquearCola = pthread_mutex_unlock(&nodoAux->semaforos.bloqueaColaDisco);
		}
		nodoAux = nodoAux->siguiente;
		i++;
	}

}

int32_t encolarEnTodosLosDiscos (int32_t socket, int32_t estado, t_NIPC paquete)
{
	extern tLista *listaDiscos;
	extern tConfig config;
	extern t_log* loguear;
	tNodoLista *ptrNodoLista = NULL;
	int32_t bloquearCola;


	ptrNodoLista = listaDiscos->head;

	while (ptrNodoLista != NULL)
	{
		bloquearCola = pthread_mutex_lock(&(ptrNodoLista->semaforos.bloqueaColaDisco));
		bloquearCola = pthread_mutex_lock(&(ptrNodoLista->semaforos.bloqueaCantPedidos));
		encolarPeticionColaDisco(ptrNodoLista->colaDisco, socket, estado, paquete);
		ptrNodoLista->cantPedidos++;
		sem_post(&ptrNodoLista->semaforos.semaforoDisco);
		bloquearCola = pthread_mutex_unlock(&(ptrNodoLista->semaforos.bloqueaCantPedidos));
		bloquearCola = pthread_mutex_unlock(&(ptrNodoLista->semaforos.bloqueaColaDisco));
		ptrNodoLista = ptrNodoLista->siguiente;
	}

	if (config.flagConsola == 1)
		log_info(loguear, "PRAID", "Se hizo un write en todos los discos");
	return 0;
}

void encolarPedidoLectura (int32_t socket, int32_t estado, t_NIPC paquete)
{
	extern tLista *listaDiscos;
	extern tConfig config;
	extern t_log* loguear;
	extern pthread_mutex_t bloqueaListaDiscos;
	int32_t bloquearCola;
	int32_t nroSector = paquete.nroSector;
    tNodoLista *ptrNodo = NULL;
    tNodoLista *discoParaEncolar = NULL;
    int32_t minimo;

    pthread_mutex_lock(&bloqueaListaDiscos);

    ptrNodo = listaDiscos->head;
    minimo = ptrNodo->cantPedidos;

    while (ptrNodo != NULL)
	{
    	if (ptrNodo->cantPedidos <= minimo && nroSector < ptrNodo->sincro)
    	{
    		minimo = ptrNodo->cantPedidos;
    		discoParaEncolar = ptrNodo;
    	}
    	ptrNodo = ptrNodo->siguiente;
	}

    pthread_mutex_unlock(&bloqueaListaDiscos);

    bloquearCola = pthread_mutex_lock(&(discoParaEncolar->semaforos.bloqueaColaDisco));
    bloquearCola = pthread_mutex_lock(&(discoParaEncolar->semaforos.bloqueaCantPedidos));
    encolarPeticionColaDisco (discoParaEncolar->colaDisco, socket, estado, paquete);
    discoParaEncolar->cantPedidos++;
	sem_post(&discoParaEncolar->semaforos.semaforoDisco);
	bloquearCola = pthread_mutex_unlock(&(discoParaEncolar->semaforos.bloqueaCantPedidos));
	bloquearCola = pthread_mutex_unlock(&(discoParaEncolar->semaforos.bloqueaColaDisco));

}

void buscarPaqueteYDesencolar (t_NIPC paquete, tNodoLista *ptrDisco, int32_t *socket, int32_t *estado)
{
	int32_t bloquearCola;

	bloquearCola = pthread_mutex_lock(&(ptrDisco->semaforos.bloqueaColaDisco));
	bloquearCola = pthread_mutex_lock(&(ptrDisco->semaforos.bloqueaCantPedidos));

	tNodo *aux = ptrDisco->colaDisco->ant;
	tNodo *ant = ptrDisco->colaDisco->ant;
	while (strcmp(aux->paquete.DescriptorID, paquete.DescriptorID) != 0)
	{
		printf("ID %s / ID :%s\n",aux->paquete.DescriptorID,paquete.DescriptorID);
		ant = aux;
		aux = aux->siguiente;
	}

	*socket = aux->nroSocketFS;
	*estado = aux->estado;

	if(ant == aux)
		ptrDisco->colaDisco->ant = ptrDisco->colaDisco->ant->siguiente;
	else
		ant->siguiente = aux->siguiente;

	NIPC_destruirpaquete(&aux->paquete);

	free(aux);
	aux = NULL;

	ptrDisco->cantPedidos--;

	bloquearCola = pthread_mutex_unlock(&(ptrDisco->semaforos.bloqueaCantPedidos));
	bloquearCola = pthread_mutex_unlock(&ptrDisco->semaforos.bloqueaColaDisco);
}


/* Bit Vector */

void actualizarBitVector (int32_t **bitVector, t_NIPC paquete, int32_t tamBitVector)
{
	div_t posicion;
	posicion = div (paquete.nroSector, tamBitVector);
	*(*bitVector + posicion.rem) = 1;
}

int32_t bitVectorCompleto(int32_t *bitVector, int32_t tamBitVector)
{
	int32_t bandera = 1;
	int32_t i=0;
	while (i < tamBitVector && bandera == 1)
	{
		if (bitVector[i] == 0)
			bandera = 0;
		i++;
	}
	return bandera;
}


/* Cuando se cae un Disco */
// TODO: (to do=para hacer) ver funcion. free()?

void desencolarDisco (int32_t nroSocketPD) // poner mutex
{
	extern tLista *listaDiscos;
	extern t_log *loguear;
	extern tConfig config;
	extern pthread_mutex_t bloqueaListaDiscos;
	int32_t bloquearLista;
	tNodoLista *ptrNodo;
	tNodoLista *ptrNodoAnt = NULL;
	tNodoLista *ptrNodoAux;
	ptrNodo = listaDiscos->head;
	bloquearLista = pthread_mutex_lock(&bloqueaListaDiscos);
	while (ptrNodo != NULL)
	{

		if (ptrNodo->nroSocketPD == nroSocketPD)
		{
			if (config.flagConsola == 0)
			log_info(loguear, "PRAID", "Se cayó un disco su identificador es %d, redistribuyendo los pedidos...",ptrNodo->idDisco);
			if (listaDiscos->head->siguiente == NULL)
			{
				ptrNodoAux = listaDiscos->head;
				listaDiscos->head = listaDiscos->head->siguiente;
				ptrNodo = NULL;
			}
			else if(ptrNodoAnt == NULL )
			{
                ptrNodoAux = ptrNodo;
                listaDiscos->head  = listaDiscos->head->siguiente;
                ptrNodo     = listaDiscos->head;
			}
			else
			{
                ptrNodoAux        = ptrNodo;
                ptrNodo            = ptrNodo->siguiente;
               	ptrNodoAnt->siguiente  = ptrNodo;
			}
		}
		else
		{
        ptrNodoAnt = ptrNodo;
        ptrNodo     = ptrNodo->siguiente;
		}
	}
	bloquearLista = pthread_mutex_unlock(&bloqueaListaDiscos);
}

int32_t algunDiscoSincronizado()
{
	extern tLista *listaDiscos;
	extern int32_t sincro;
	int32_t bandera = 0;
	tNodoLista *ptrLista;
	ptrLista = listaDiscos->head;
	while (bandera == 0 && ptrLista != NULL)
	{
		if (ptrLista->sincro == sincro)
			bandera = 1;
		ptrLista = ptrLista->siguiente;
	}
	return (bandera);
}

void redistribuirPedidosDiscoCaido (tCola *colaDePeticiones)
{
	extern sem_t semaforoLista;
	tNodo *ptrNodo;

	while (colaDePeticiones->ant != NULL && colaDePeticiones->post != NULL)
	{
		ptrNodo = colaDePeticiones->ant;
		colaDePeticiones->ant = colaDePeticiones->ant->siguiente;
		ptrNodo->siguiente = NULL;
		if (ptrNodo->paquete.PayloadDescriptor == LEERSECTOR)
		{
			encolarPeticionColaGral(ptrNodo->nroSocketFS, ESPERA, ptrNodo->paquete);
			sem_post(&semaforoLista);
		}
		NIPC_destruirpaquete(&ptrNodo->paquete);
		free(ptrNodo);
		ptrNodo = NULL;
	    if (colaDePeticiones->ant == NULL)
	    	colaDePeticiones->post = NULL;
	}
}

int32_t *parsearSectores(char *cadenaAseparar, int32_t *cantidad)
{
	char *spTokenSafe;
	char *palabra;
	int32_t *sectores;
	int32_t contador=0;
	sectores = (int32_t *) calloc (5, sizeof(int32_t));

	palabra = (strtok_r(cadenaAseparar," ", &spTokenSafe));
	while (palabra != NULL)
	{
		sectores[contador] = atoi (palabra);
		palabra = (strtok_r(NULL," ", &spTokenSafe));
		contador++;
	}
	sectores = (int32_t *) realloc (sectores, contador * sizeof(int32_t));
	*cantidad = contador;
	return sectores;
}
